package org.displayextended
{
	import flash.display.Sprite;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	import org.event.BuildCompleteEvent;
	import org.vo.ModalVO;
	
	/**
	 * 
	 * @author zachary.seabrook
	 * 
	 */
	public class AniMovSpriteManager extends EventDispatcher
	{
		// Trace shortcut method and toggle property
		public var showTrace:Boolean = false;
		public function t( ...rest ):void{ if( showTrace) trace( this, rest ); }
		
		// Constants
		public static const IN:String = 'in';
		public static const OUT:String = 'out';
		
		// Targets
		public var parentContainer:Sprite;
		private static var instance:AniMovSpriteManager;
		
		// Load/Unload
		public var current:AniMovSprite;
		public var next:AniMovSprite;

		// Chain
		public var sequenceChain:Array;
		private var direction:String;
		public var chain:Array;
		
		// Chain/Sequence
		public var sequenceDelays:Array;
		public var sequenceDelay:int;
		private var timer:Timer;
		private var delays:Array;
		private var buildHolder:AniMovSprite;
		
		// Chain/Simultaneous
		private var buildHolders:Array;
		private var buildHolderCount:int;
		
		// Modal 
		private static var _modalProperties:ModalVO;
		private static var _modal:Modal;
		private static var _modalRequests:int;
		private static var _modalHolder:Sprite;
		
		// Flags
		private var _transitioning:Boolean = false;
		public var animateChain:Boolean = false;
		public var useModal:Boolean = true;
		public var useSequenceIn:Boolean = false;
		public var useSequenceOut:Boolean = false;
		
		public function AniMovSpriteManager( container:Sprite )
		{
			if( !container )
				throw new Error( 'AnimatedMovieSpriteManager must have a parent DisplayObject.' );
				
			parentContainer = container;
			reset();
			
			if( !_modalHolder )
				_modalHolder = parentContainer;
			
			if( isNaN(_modalRequests) )
				_modalRequests = 0;
				
			if( !instance )
				instance = this;
				
			showTrace = true;
		}
		
		public function load( sprite:AniMovSprite ):void
		{
			t( 'load', sprite, current );
			
			setTransitioning( true );
			next = sprite;
			
			if( current && !next.isModal )
				doCurrentBuildOut();
			else
				doNextBuildIn();
		}
		
		public function unload():void
		{
			t( 'unload', current, next );
			
			setTransitioning( true );
			current.addEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onUnloadCurrentBuildOut );
			current.buildOut();
		}
		
		public function reset():void
		{
			sequenceDelay = 0;
			sequenceChain = [];
			sequenceDelays = [];
		}
		
		public function setItems( arr:Array ):void
		{
			for each( var obj:Object in arr )
			{
				add( obj );
			}
		}
		
		public function add( obj:Object, delay:int=0 ):void
		{
			if( !sequenceChain )
				sequenceChain = [];
			
			with( obj as AniMovSprite )
			{
				sequenceChain.push( obj );
			}
			
			if( !sequenceDelays )
				sequenceDelays = [];
			
			sequenceDelays.push( delay );
		}
		
		public function sequenceIn( ...objs ):void
		{
			direction = IN;
			initSequence( objs );
		}
		
		public function sequenceOut( ...objs ):void
		{
			direction = OUT;
			initSequence( objs );
		}
		
		private function doNextBuildIn():void
		{
			t( 'doNextSpriteBuildIn' );
			
			var holder:AniMovSprite = current;
			current = next;
			next = holder;
			parentContainer.addChild( current );
				
			if( _modal && useModal && parentContainer == _modalHolder )
			{				
				parentContainer.swapChildren( _modal, current );
			}
			
			current.addEventListener( BuildCompleteEvent.BUILD_IN_COMPLETE, onNextBuildIn );
			current.buildIn();
		}
		
		private function onNextBuildIn( event:BuildCompleteEvent ):void
		{
			t( 'onNextScreenBuildInComplete', current );
			
			current.removeEventListener( BuildCompleteEvent.BUILD_IN_COMPLETE, onNextBuildIn );
			setTransitioning( false );
		}
		
		private function doCurrentBuildOut():void
		{
			t( 'doCurrentSpriteBuildOut' );
			
			current.addEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onCurrentBuildOut );
			current.buildOut();
		}
		
		private function onCurrentBuildOut( event:BuildCompleteEvent ):void
		{
			t( 'onCurrentScreenBuildOutComplete', current );
			
			current.removeEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onCurrentBuildOut );
			
			if( parentContainer.contains( current ) )
				parentContainer.removeChild( current );
			
			t( Children.list( parentContainer ) );
			
			var c:Array = Children.gather( parentContainer );
			
			if( c.length > 1 )
			{
				current = c.shift() as AniMovSprite;
				doCurrentBuildOut();
				
			}
			else
			{
				doNextBuildIn();
			}

		}
		
		private function onUnloadCurrentBuildOut( event:BuildCompleteEvent ):void
		{
			t( 'onUnloadScreenBuildOutComplete', current );
			
			current.removeEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onCurrentBuildOut );
			
			if( parentContainer.contains( current ) )
				parentContainer.removeChild( current );
			
			if( next )
				current = next;
			
			setTransitioning( false );
		}
		
		private function initSequence( objs:Array ):void
		{
			t( 'initSequence' );
			
			if( objs.length > 0 )
			{
				for each( var obj:Object in objs )
				{
					add( obj );
				}
			}
			
			if( sequenceChain.length > 0 )
			{
				setTransitioning( true );
				cloneSequence();
				
				switch( direction )
				{
					case IN :
						if( useSequenceIn )
							sequence();
						else
							simultaneous();	
					break;
						
					case OUT :
						if( useSequenceOut )
							sequence();
						else
							simultaneous();
					break;
				}
			}
		}
		
		private function cloneSequence():void
		{
			chain = new Array();
			
			if( direction == IN )	
				sequenceChain.reverse();
				
			for each( var cobj:Object in sequenceChain )
			{
				chain.push( cobj );
			}
			
			if( direction == IN )	
				sequenceChain.reverse();
			
			delays = new Array();
				
			if( direction == IN )
				sequenceDelays.reverse();
				
			for each( var dobj:Object in sequenceDelays )
			{
				delays.push( dobj );
			}
			
			if( direction == IN )
				sequenceDelays.reverse();
		}
		
		private function simultaneous():void
		{
			t( 'simultaneous', chain.length );
			
			buildHolderCount = 0;
			
			if( chain.length > 0 )
			{
				for each( var link:AniMovSprite in chain )
				{
					switch( direction )
					{
						case IN :
							parentContainer.addChild( link );
							link.addEventListener( BuildCompleteEvent.BUILD_IN_COMPLETE, onSimultaneousComplete );
							link.buildIn();
						break;
						
						case OUT :
							link.addEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onSimultaneousComplete );
							link.buildOut();
						break;
					}
				}
			}
			
		}
		
		private function sequence():void
		{
			t( 'sequence', chain.length );
			
			if( chain.length > 0 )
			{
				var delay:int = delays.pop();
				delay > 0 ? delay = delay : delay = sequenceDelay;
				
				if( delay > 0 )
				{
					delayNext( delay );
				}
				else
				{
					runNext();
				}
			}
			else
			{
				switch( direction )
				{
					case IN :
						dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.SEQUENCE_IN_COMPLETE ) );
					break;
					
					case OUT :
						dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.SEQUENCE_OUT_COMPLETE ) );
					break;
				}
				
				setTransitioning( false );
			}
		}
		
		private function runNext():void
		{
			t( 'runNext' );
		
			buildHolder = chain.pop();
		
			switch( direction )
			{
				case IN :
					parentContainer.addChild( buildHolder );
					buildHolder.addEventListener( BuildCompleteEvent.BUILD_IN_COMPLETE, onSequenceComplete );
					buildHolder.buildIn();
				break;
				
				case OUT :
					buildHolder.addEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onSequenceComplete );
					buildHolder.buildOut();
				break;
			}
		}
		
		private function delayNext( delay:int ):void
		{
			if( !timer )
			{
				timer = new Timer( delay, 1 );
				timer.addEventListener( TimerEvent.TIMER_COMPLETE, onDelayNextComplete );
			}
			else if( timer.delay != delay )
			{
				timer.delay = delay;
			}
			
			timer.start();
		}
		
		private function onDelayNextComplete( evt:TimerEvent ):void
		{
			runNext();
		}
		
		private function onSimultaneousComplete( evt:BuildCompleteEvent ):void
		{
			t( 'onSimultaneousComplete', buildHolderCount, chain.length );
			
			buildHolderCount++;
			
			if( buildHolderCount == chain.length )
			{
				t( '----> onSimultaneousComplete', direction, chain );
				
				
				for each( var link:AniMovSprite in chain )
				{
					switch( direction )
					{
						case IN :
							link.removeEventListener( BuildCompleteEvent.BUILD_IN_COMPLETE, onSimultaneousComplete );
						break;
						
						case OUT :
							link.removeEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onSimultaneousComplete );
							parentContainer.removeChild( link );
						break;
					}
				}
				
				switch( direction )
				{
					case IN :
						dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.SEQUENCE_IN_COMPLETE ) );
					break;
					
					case OUT :
						dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.SEQUENCE_OUT_COMPLETE ) );
					break;
				}
				
				setTransitioning( false );
			}
		}
		
		private function onSequenceComplete( evt:BuildCompleteEvent ):void
		{
			t( 'onSequenceComplete' );
			
			if( direction == OUT )
			{
				buildHolder.removeEventListener( BuildCompleteEvent.BUILD_OUT_COMPLETE, onSequenceComplete );
				parentContainer.removeChild( buildHolder );
			}
			else
			{
				buildHolder.removeEventListener( BuildCompleteEvent.BUILD_IN_COMPLETE, onSequenceComplete );
			}
			
			sequence();
		}
		
		protected function setTransitioning( value:Boolean ):void
		{
			t( 'setTransitioning', useModal, value, _modalRequests );
			
			if( showTrace )
				Children.list( parentContainer );
			
			if( useModal )
			{
				if( value )
				{
					_modalRequests++;
					
					if( !_modal )
					{
						t('Adding Modal...' );
						_modal = new Modal( modalProperties );
						_modalHolder.addChild( _modal );
						_modal.draw();
					}
				}
				else if( _modal )
				{
					_modalRequests--;
					
					if( _modalRequests <= 0 )
					{
						t('Removing Modal...', parentContainer, direction );
						_modal.clear();
						_modalHolder.removeChild( _modal );
						_modal = null;
						
						if( direction == IN )
						{
							dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.BUILD_IN_COMPLETE ) );
						}
						else if( direction == OUT )
						{
							dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.BUILD_OUT_COMPLETE ) );
						}
						
						instance.dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.BUILD_COMPLETE ) );
					}
				}
			}
			else if( !value )
			{
				t('[AnimMovSpriteManager] BuildComplete.' );
				dispatchEvent( new BuildCompleteEvent( BuildCompleteEvent.BUILD_COMPLETE ) );
			}
			
			_transitioning = value;
		}
		
		public function get transitioning():Boolean { return _transitioning; }
		
		public function set modalProperties( val:ModalVO ):void
		{
			 _modalProperties = val;
		}
		
		public function get modalProperties():ModalVO
		{
			if( !_modalProperties ) 
				modalProperties = new ModalVO;
			return _modalProperties;
		}
	}
}